<?php

namespace app\common\library;

/**
 * 微信小程序支付
 */
class wxPay
{   
    /**
     * 微信小程序支付
     * 
     * @param   $total_fee     订单总金额(单位为分)     
     * @param   $out_trade_no  我方订单号(不可重复)     
     * @param   $trade_type    交易类型 JSAPI--JSAPI支付（或小程序支付）     
     * @param   $openid        trade_type=JSAPI，此参数必传，用户在商户appid下的唯一标识。     
     * @param   $body          商品描述
     * 
     * @return array   
     *       
     */
    public function getPay($total_fee = 1 ,$out_trade_no = '',$trade_type='JSAPI',$openid='',$body='会员充值')
    {
        
        $mch_id     = Config('wxpay.mch_id'); //商户ID
        $pay_key    = Config('wxpay.pay_key'); //商户密钥
        $appid      = Config('wxpay.appid'); //微信分配的小程序ID
        $nonce_str  = md5(uniqid(md5(microtime(true)),true)); //生成随机字符串,长度要求在32位以内
        $notify_url = Config('wxpay.notify_url'); //异步接收微信支付结果通知的回调地址

        $data=[
            'appid'=>$appid,
            'mch_id'=>$mch_id,
            'nonce_str'=>$nonce_str,
            'body'=>$body,
            'openid'=>$openid,
            'out_trade_no'=>$out_trade_no,
            'total_fee'=>$total_fee,
            'spbill_create_ip'=>$_SERVER['SERVER_ADDR'],
            'notify_url'=>$notify_url,
            'trade_type'=>$trade_type,
        ];
        //生成支付签名
        $data['sign'] = $this->MakeSign($data,$pay_key);
        
        $post_xml = $this->ArrayToXml($data); //将数组转换为数组xml数据
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $xml = $this->http_request($url,$post_xml); //POST请求
        $return_data= $this->XmlToArray($xml); //将xml数据转换为数组

        if($return_data['return_code'] == 'SUCCESS'){
            $re_array=[
                        'appId'  =>$appid,
                        'nonceStr'=>$nonce_str,                      
                        'package'=>'prepay_id='.$return_data['prepay_id'],
                        'signType'=>'MD5',
                        'timeStamp'=>(String)time(), //注意要String类型
                        ];
            $re_array['paySign'] = $this->MakeSign($re_array,$pay_key);
            return ['status'=>true,'msg'=>'发起支付成功','data'=>$re_array];

        }else{
            $msg = isset($return_data['return_msg']) ? $return_data['return_msg']:'发起支付失败';
            return ['status'=>false,'msg'=>$msg,'data'=>[]];
        }
    }

    /**
     * 生成签名
     * @param   $params 加密数组
     * @param   $KEY    支付key
     *
     * @return  $result  签名值
     * 
     */
    public function MakeSign($params, $keys)
    {
        //第一步：按字典序排序数组参数
        ksort($params);
        //参数进行拼接key=value&k=v
        $string = '';
        if (!empty($params)) {
            $array = array();
            foreach ($params as $key => $value) {
                $array[] = $key . '=' . $value;
            }
            $string = implode("&", $array);
        }
        //第二步：在string后加入keys
        $string = $string . "&key=" . $keys;
        //第三步：MD5加密
        $string = md5($string);
        //第四步：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

   /**
	 * 输出xml字符
	 * @throws WxPayException
	**/
	public function ArrayToXml($values)
	{
		if(!is_array($values) || count($values) <= 0)
		{
            return ['status'=>false,'msg'=>"数组数据异常！",'data'=>[]];
    	}
    	
    	$xml = "<xml>";
    	foreach ($values as $key=>$val)
    	{
    		if (is_numeric($val)){
    			$xml.="<".$key.">".$val."</".$key.">";
    		}else{
    			$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    		}
        }
        $xml.="</xml>";
        return $xml; 
	}

    /**
     * 将xml转为array
     * @param string $xml
     * @throws WxPayException
     */
	public function XmlToArray($xml)
	{	
		if(!$xml){
            return ['status'=>false,'msg'=>"xml数据异常！",'data'=>[]];
		}
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
		return $this->values;
	}
	

     /**
     * curl封装
     * @param string $url
     * @param array  $arr
     * @return mixed $ret
     */
    public static function http_request($url,$arr)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS,$arr);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);;
        //执行并获取接口传的内容
        $ret = curl_exec($ch);
        curl_close($ch);
        return $ret;
    }

}
